package org.example.what_mall.service.impl;

import com.auth0.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.example.what_mall.dto.GoodsDTO;
import org.example.what_mall.dto.OrderForm;
import org.example.what_mall.dto.SettleForm;
import org.example.what_mall.dto.SettleOrderForm;
import org.example.what_mall.entity.Category;
import org.example.what_mall.entity.Goods;
import org.example.what_mall.entity.Order;
import org.example.what_mall.entity.Spec;
import org.example.what_mall.enums.OrderState;
import org.example.what_mall.exception.GlobalException;
import org.example.what_mall.mapper.CategoryMapper;
import org.example.what_mall.mapper.GoodsMapper;
import org.example.what_mall.mapper.OrderMapper;
import org.example.what_mall.mapper.SpecMapper;
import org.example.what_mall.service.IMallService;
import org.example.what_mall.util.RedisConstant;
import org.example.what_mall.util.RedisLock;
import org.example.what_mall.util.UserHolder;
import org.example.what_mall.util.WordUtil;
import org.example.what_mall.vo.GoodsVO;
import org.example.what_mall.vo.OrderVO;
import org.example.what_mall.vo.SpecVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

@Service
public class MallServiceImpl implements IMallService {
    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private SpecMapper specMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisLock redisLock;

    @Override
    public List<GoodsVO> getGoodsByType(Integer typeId) {
        List<GoodsVO> goodsVOList = new ArrayList<>();

        if(typeId == -1) {
            List<Goods> goods = goodsMapper.selectList(new LambdaQueryWrapper<Goods>().last("limit 10"));
            for(Goods good : goods) {
                Spec spec = specMapper.selectOne(new LambdaQueryWrapper<Spec>().eq(Spec::getGoodsId, good.getId()).last("limit 1"));
                GoodsVO goodsVO = GoodsVO.of(good, spec.getUnitPrice());
                goodsVOList.add(goodsVO);
            }
            return goodsVOList;
        }
        List<Goods> goods = goodsMapper.selectList(new LambdaQueryWrapper<Goods>().eq(Goods::getTypeId, typeId));

        for (Goods good : goods) {
            Spec spec = specMapper.selectOne(new LambdaQueryWrapper<Spec>().eq(Spec::getGoodsId, good.getId()).last("LIMIT 1"));
            GoodsVO goodsVO = GoodsVO.of(good, spec.getUnitPrice());
            goodsVOList.add(goodsVO);
        }
        return goodsVOList;

    }

    @Override
    public GoodsDTO getGoodsInfo(Integer id) {
        System.out.println(id);
        Goods goods = goodsMapper.selectById(id);
        List<Spec> specs = specMapper.selectList(new LambdaQueryWrapper<Spec>().eq(Spec::getGoodsId, id));
        List<SpecVO> specVOList = new ArrayList<>();
        for (Spec spec : specs) {
            SpecVO specVO = new SpecVO();
            specVO.setId(spec.getId());
            specVO.setSpecName(spec.getSpecName());
            specVO.setStockNum(spec.getStockNum());
            specVO.setUnitPrice(spec.getUnitPrice());
            specVOList.add(specVO);
        }
        GoodsDTO goodsDTO = new GoodsDTO();
        goodsDTO.setId(goods.getId());
        goodsDTO.setName(goods.getName());
        goodsDTO.setImg(goods.getImg());
        goodsDTO.setDesc(goods.getDesc());
        goodsDTO.setSpecs(specVOList);
        goodsDTO.setTypeId(goods.getTypeId());
        return goodsDTO;

    }

    @Override
    public List<GoodsVO> searchGoods(String keyword) {

        List<String> words = WordUtil.getInstance().word(keyword);
        Set<GoodsVO> goodsSet = new HashSet<>();
        for (String word : words) {
            Set<String> ids = stringRedisTemplate.opsForSet().members(RedisConstant.GOODS_WORD_PREFIX + word);
            if(ids == null || ids.isEmpty()) continue;
            for (String id : ids) {
                Goods goods = goodsMapper.selectById(id);
                if(goods == null) continue;
                Spec spec = specMapper.selectOne(new LambdaQueryWrapper<Spec>().eq(Spec::getGoodsId, id).last("limit 1"));
                GoodsVO goodsVO = GoodsVO.of(goods, spec.getUnitPrice());
                goodsSet.add(goodsVO);
            }
        }
        return new ArrayList<>(goodsSet);
    }

    @Transactional
    @Override
    public void addOrder(OrderForm form) {
        String keyword = RedisConstant.USER_TOKE_PREFIX+form.getToken();
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(keyword);
        Integer buyerId = Integer.parseInt(entries.get("id").toString());

        if (!redisLock.tryLock(form.getGoodsDetailId().toString(), 30L)) {
            throw new GlobalException(400, "获取失败");
        }
        Spec spec = specMapper.selectOne(new LambdaQueryWrapper<Spec>().eq(Spec::getId, form.getGoodsDetailId()));
        if(spec==null || spec.getStockNum() < form.getNum()){
            throw new GlobalException(400, "库存不足/参数错误");
        }

        specMapper.update(new LambdaUpdateWrapper<Spec>()
                .eq(Spec::getId, spec.getId())
                .eq(Spec::getStockNum, spec.getStockNum())
                .set(Spec::getStockNum, spec.getStockNum()-form.getNum()));

        Order order = Order.builder()
                .state(form.getState())
                .amount(form.getAmount())
                .buyerId(buyerId)
                .goodsDetailId(form.getGoodsDetailId())
                .num(form.getNum())
                .createtime(LocalDateTime.now())
                .build();
        orderMapper.insert(order);
        redisLock.unlock(order.getGoodsDetailId().toString());
    }

    @Override
    public List<OrderVO> getOrderByState(Integer state, String token) {
        String keyword = RedisConstant.USER_TOKE_PREFIX+token;
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(keyword);
        Integer uerId = Integer.parseInt(entries.get("id").toString());

        List<OrderVO> orderVOList = new ArrayList<>();

        if(state == OrderState.ALL.getCode()) {
            List<Order> orders = orderMapper.selectList(new LambdaQueryWrapper<Order>().eq(Order::getBuyerId, uerId));
            for(Order order : orders){
                Spec spec = specMapper.selectOne(new LambdaQueryWrapper<Spec>().eq(Spec::getId, order.getGoodsDetailId()));
                if(spec == null) continue;
                Goods goods = goodsMapper.selectById(spec.getGoodsId());
                if(goods == null) continue;
                OrderVO orderVO = OrderVO.of(order, goods, spec);
                orderVOList.add(orderVO);
            }
            return orderVOList;
        }

        List<Order> orders = orderMapper.selectList(new LambdaQueryWrapper<Order>()
                .eq(Order::getState, state)
                .eq(Order::getBuyerId, uerId));

        for(Order order : orders){
            Spec spec = specMapper.selectOne(new LambdaQueryWrapper<Spec>().eq(Spec::getId, order.getGoodsDetailId()));
            if(spec == null) continue;
            Goods goods = goodsMapper.selectById(spec.getGoodsId());
            if(goods == null) continue;
            OrderVO orderVO = OrderVO.of(order, goods, spec);
            orderVOList.add(orderVO);
        }

        return orderVOList;
    }

    @Override
    public void settleAccounts(SettleForm form) {
        List<SettleOrderForm> cartList = form.getCartList();
        List<Integer> orderIds = cartList.stream().map(SettleOrderForm::getId).toList();
        orderMapper.update(new LambdaUpdateWrapper<Order>().in(Order::getId, orderIds).set(Order::getState, OrderState.UNSHIPPED.getCode()));
    }

    @Override
    public List<Category> getTypes() {
        List<Category> categories = categoryMapper.selectList(null);
        System.out.println(categories);
        return categories;
    }

    @Transactional
    @Override
    public void confirmReceive(Integer id) {
        Integer userId = UserHolder.getUser().getId();
        orderMapper.update(new LambdaUpdateWrapper<Order>()
                .eq(Order::getId, id)
                .eq(Order::getBuyerId, userId)
                .set(Order::getState, OrderState.ALL.getCode()));
    }
}
